package com.bms.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * @author Lan
 */
public class FileUtil extends FilenameUtils {

	protected static Logger logger = Logger.getLogger(FileUtil.class);

	/**
	 * 根据系统格式化文件路径
	 * 
	 * @param path
	 *            文件路径
	 * @return 格式化后的文件路径(windows:"\", linux:"/")。路径为空则返回""
	 */
	public static String formatPath(String path) {
		path = StringUtils.trimToEmpty(path);
		return separatorsToSystem(path);
	}

	/**
	 * 获取父路径
	 * 
	 * @param path
	 *            文件路径
	 * @return 文件父路径
	 */
	public static String getParentPath(String path) {
		String parent = "";
		try {
			File file = new File(path);
			parent = formatPath(file.getParent());
		} catch (Exception e) {
			logger.error("文件路径不合法[path=" + path + "]", e);
		}
		return parent;
	}

	/**
	 * 创建文件所在的父目录，或创建指定的目录
	 * 
	 * @author Lan
	 * @param fileName 文件全路径
	 * <pre>
	 * 若是文件路径，则创建其父目录；若是目录路径，则直接创建
	 * 注：文件路径不以"/"或"\"结束，否则会以文件名作为目录名创建
	 *     目录必须以"/"或"\"结束，否则会创建父级目录
	 * </pre>
	 */
	public static boolean createDir(String fileName) {
		boolean isSucc = false;
		if(StringUtils.isNotBlank(fileName)){
			String path = formatPath(getFullPath(fileName));
			File directory = new File(path);
			try {
				FileUtils.forceMkdir(directory);
				isSucc = true;
			} catch (IOException e) {
				logger.error("创建目录失败:", e);
			}
		}else{
			logger.error("不能创建目录, 参数fileName为空！");
		}
		return isSucc;
	}
	
	/**
	 * 获取指定目录下的文件，不获取子目录的文件
	 * @author Lan
	 * @param dirPath 目录路径
	 * @return 该目录下的文件
	 */
	public static File[] getFilesOfDir(String dirPath) {
		File file = new File(dirPath);
		File[] files = file.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				File f = new File(dir, name);
				if (f.isDirectory()) {
					return false;
				} else {
					return true;
				}
			}
		});
		return files;
	}

	/**
	 * 获取指定目录下的所有目录，包括子目录
	 * @author Lan
	 * @param resultDirList 存放所有目录（包括子目录）
	 * @param rootDir 指定目录
	 */
	public static void getAllDirectory(List<File> resultDirList, File rootDir) {
		File[] dirs = rootDir.listFiles();
		for (File f : dirs) {
			if (f.isDirectory()) {
				resultDirList.add(f);
				getAllDirectory(resultDirList, f);
			} else {
			}
		}
	}

	/**
	 * 获取指定根路径下的所有文件（递归获取子目录下的文件）
	 * 
	 * @author Lan
	 * @param resultFileList
	 *            存放所有文件的集合
	 * @param rootDir
	 *            需要遍历的根目录
	 */
	public static void getAllFiles(List<File> resultFileList, File rootDir) {
		File[] dirs = rootDir.listFiles();
		for (File f : dirs) {
			if (f.isDirectory()) {
				getAllFiles(resultFileList, f);
			} else {
				resultFileList.add(f);
			}
		}
	}

	/**
	 * 判断一个文件是否存在
	 * @author Lan
	 * @param filePath
	 *            文件路径
	 * @return 存在返回true，否则返回false
	 */
	public static boolean isExist(String filePath) {
		return new File(filePath).exists();
	}

	/**
	 * 判断文件是否是指定正则表达式的后缀
	 * @author Lan
	 * @param filename 带后缀名的文件名（可包括目录路径）
	 * @param regex "jsp|html|htm"
	 * @return
	 */
	public static boolean isSpecifiedExt(String filename, String regex){
		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(filename);
		boolean isMatched = m.find();
//		logger.debug("判断" + filename + "的后缀是否匹配正则[" + regex + "]：" + isMatched);
		return isMatched;
	}
	
	/**
	 * 移动文件或者目录 至 另一个目录下。源文件或源目录若不存在，不移动，目标目录不存在，则创建
	 * @author Lan
	 * @param srcPath 源文件/源目录
	 * @param distFolder 目标目录
	 */
	public static boolean moveFileOrDir(String srcPath, String distFolder) {
		boolean isSucc = false;
		String logstr = "[" + srcPath + "->" + distFolder + "]";
		try {
			File resFile = new File(srcPath);
			File distFile = new File(distFolder);
			if (resFile.isDirectory()) {
				FileUtils.moveDirectoryToDirectory(resFile, distFile, true);
				logger.debug("移动目录成功" + logstr);
			} else if (resFile.isFile()) {
				FileUtils.moveFileToDirectory(resFile, distFile, true);
				logger.debug("移动文件成功" + logstr);
			}
		} catch (IOException e) {
			logger.error("移动失败" + logstr, e);
			throw new RuntimeException(e);
		}
		return isSucc;
	}

	/**
	 * 复制文件或者目录至另一目录,复制前后文件完全一样。源文件或目录若不存在，不复制
	 * 
	 * @param srcPath
	 *            源文件/目录路径
	 * @param distFolder
	 *            目标文件夹
	 */
	public static boolean copyFileOrDir(String srcPath, String distFolder) {
		boolean isSucc = false;
		String logstr = "[" + srcPath + "->" + distFolder + "]";
		try {
			File resFile = new File(srcPath);
			File distFile = new File(distFolder);
			if (resFile.isDirectory()) {
				FileUtils.copyDirectoryToDirectory(resFile, distFile);
				logger.debug("复制目录成功" + logstr);
			} else if (resFile.isFile()) {
				FileUtils.copyFileToDirectory(resFile, distFile, true);
				logger.debug("复制文件成功" + logstr);
			}
			isSucc = true;
		} catch (IOException e) {
			logger.error("复制失败" + logstr, e);
			throw new RuntimeException(e);
		}
		return isSucc;
	}

	/**
	 * 删除一个文件或者目录
	 * 
	 * @param targetPath
	 *            文件或者目录路径
	 * @IOException 当操作发生异常时抛出
	 */
	public static void deleteFile(String targetPath) throws IOException {
		File targetFile = new File(targetPath);
		if (targetFile.isDirectory()) {
			FileUtils.deleteDirectory(targetFile);
		} else if (targetFile.isFile()) {
			targetFile.delete();
		}
	}
	
	public static boolean rename(String srcFilePath, String newName){
		boolean isSucc = false;
		String newFilePath = getFullPath(srcFilePath) + newName;
		File srcFile = new File(srcFilePath);
		if(!isExist(newFilePath)){
			File destFile = new File(newFilePath);
			isSucc = srcFile.renameTo(destFile);
		} else {
			logger.error("重命名的文件名已存在");
		}
		return isSucc;
	}
	
	/**
	 * 判断指定文件是否存在，否存在，则重命名
	 * @author Lan
	 * @param srcFilePath 源文件的全路径
	 * @return 源文件的全路径/重命名后的文件全路径
	 */
	public static String rename(String srcFilePath){
		String newPath = srcFilePath;
		
		String oldName = FileUtil.getBaseName(srcFilePath);//不带后缀名的文件名称
		String extension = FileUtil.getExtension(srcFilePath);//获取后缀名
		String parentPath = getFullPath(srcFilePath);
		
		int i = 1;
		while(isExist(newPath)){
			newPath = parentPath + StringUtils.split(oldName, "_")[0] + "_" + (i++) + "." + extension;
		}
		return newPath;
	}
	
	/**
	 * 获取单个文件的MD5值！
	 * @author Lan
	 * @param file
	 * @return MD5值
	 */
	public static String getFileMD5(File file) {
		String md5Str = "";
		if(file != null){
			if(file.exists()){
				if(file.isFile()){
					MessageDigest digest = null;
					FileInputStream in = null;
					byte buffer[] = new byte[1024];
					int len;
					try {
						digest = MessageDigest.getInstance("MD5");
						in = new FileInputStream(file);
						while ((len = in.read(buffer, 0, 1024)) != -1) {
							digest.update(buffer, 0, len);
						}
						
						BigInteger bigInt = new BigInteger(1, digest.digest());
						md5Str = bigInt.toString(16);
						
					} catch (Exception e) {
						logger.error("获取文件MD5值出现异常", e);
					} finally {
						IOUtils.closeQuietly(in);
					}
				}else{
					logger.debug(file.getAbsoluteFile() + "不是文件类型");
				}
			}else{
				logger.debug(file.getAbsoluteFile() + "不存在");
			}
		}else{
			logger.error("参数file对象为空");
		}
		
		return md5Str;
	}

	/**
	 * 将内容写入本地文件
	 * @param file
	 * @param content
	 * @return
	 * @author Lan
	 */
	public static boolean writeFile(File file, String content) {
		boolean isSucc = false;
		BufferedWriter out = null;
		try {
			out = new BufferedWriter(new FileWriter(file, false));
			out.write(content);
			isSucc = true;
		} catch (IOException e) {
			logger.error("文件写入失败", e);
		} finally {
			IOUtils.closeQuietly(out);
		}
		return isSucc;
	}
}
